package gu.sql2java;

import static com.google.common.base.Preconditions.checkNotNull;

import java.lang.reflect.Type;

import com.google.common.reflect.TypeToken;

import gu.sql2java.exception.ResultSetCodecException;
import gu.sql2java.exception.UnsupportTypeException;

/**
 * @author guyadong
 * @since 3.21.0
 */
public abstract class BaseColumnCodec implements ColumnCodec {

	protected BaseColumnCodec() {
	}
	protected abstract <T> T doDeserialize(Object columnValue, Class<T> targetType) throws ResultSetCodecException;
	protected abstract <T> T doDeserialize(Object columnValue, Type targetType) throws ResultSetCodecException;
	protected abstract <T>T doSerialize(Object obj, Class<T> targetType) throws ResultSetCodecException;
	@SuppressWarnings("unchecked")
	@Override
	public <T> T deserialize(Object columnValue, Class<T> targetType) throws ResultSetCodecException {
		if(null == columnValue) {
			if(null != targetType &&  targetType.isPrimitive()) {
				throw new UnsupportTypeException(
						"targetType must not be primitive " + targetType.getName()+ " if columnValue is null");
			}
			return null;
		}else if(checkNotNull(targetType,"targetType is null").isInstance(columnValue)) {
			return (T)columnValue;
		}
		return doDeserialize(columnValue,targetType);
	}

	@SuppressWarnings("unchecked")
	public <T> T deserialize(Object columnValue, Type targetType) throws ResultSetCodecException {
		if(null == columnValue) {
			
			if(null != targetType &&  TypeToken.of(targetType).getRawType().isPrimitive()) {
				throw new UnsupportTypeException(
						"targetType must not be primitive " + targetType.toString()+ " if columnValue is null");
			}
			return null;
		}else if(TypeToken.of(checkNotNull(targetType,"targetType is null")).getRawType().isInstance(columnValue)) {
			return (T)columnValue;
		}
		return doDeserialize(columnValue,targetType);
		
	}
	@SuppressWarnings("unchecked")
	@Override
	public <T>T serialize(Object obj, Class<T> targetType) throws ResultSetCodecException {
		if(null == obj) {
			if(null != targetType && targetType.isPrimitive()) {
				throw new UnsupportTypeException(
						"targetType must not be primitive " + targetType.getName() + " if obj is null");
			}
			return null;
		}else if(checkNotNull(targetType,"targetType is null").isInstance(obj)) {
			return (T) obj;
		}
		return doSerialize(obj, targetType);
	}

}
